import javalang

# Java 代码示例
java_code = """package com.pingpongx.business.payout.biz.impl.outer;

import static com.pingpongx.business.payout.dependency.user.UserClient.USER_OPERATION_NOT_ALLOWED;
import static com.pingpongx.business.payout.open.api.constants.OuterConstants.*;

import java.util.Collections;

import javax.annotation.Resource;

import org.apache.dubbo.config.annotation.Service;

import com.alibaba.fastjson.JSON;
import com.pingpongx.business.account.api.constant.AccountTypeEnum;
import com.pingpongx.business.account.api.constant.CurrencyEnum;
import com.pingpongx.business.common.constant.OrderStatus;
import com.pingpongx.business.common.dto.OrderDTO;
import com.pingpongx.business.common.exception.BizException;
import com.pingpongx.business.common.exception.OuterErrorCode;
import com.pingpongx.business.payout.api.dto.request.OrderCaculateRequest;
import com.pingpongx.business.payout.api.dto.request.OrderPayRequest;
import com.pingpongx.business.payout.api.dto.response.AccountBalanceInfoResponse;
import com.pingpongx.business.payout.api.dto.response.OrderCalculateResponse;
import com.pingpongx.business.payout.api.service.PayoutQueryService;
import com.pingpongx.business.payout.api.service.PayoutService;
import com.pingpongx.business.payout.biz.common.RedisKeyUtil;
import com.pingpongx.business.payout.biz.impl.outer.util.ProcurePayConverter;
import com.pingpongx.business.payout.biz.model.ProcurePayTrailCacheDTO;
import com.pingpongx.business.payout.config.CommonConfig;
import com.pingpongx.business.payout.dal.dataobject.Order;
import com.pingpongx.business.payout.dal.repository.OrderRepository;
import com.pingpongx.business.payout.dependency.account.AccountBalanceClient;
import com.pingpongx.business.payout.dependency.util.RedisLockUtil;
import com.pingpongx.business.payout.dependency.util.RedisUtil;
import com.pingpongx.business.payout.open.api.OuterProcurePayService;
import com.pingpongx.business.payout.open.api.model.req.*;
import com.pingpongx.business.payout.open.api.model.resp.*;
import com.pingpongx.coordination.spring.Router;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @description: 网商代发支付服务实现类
 * @author: chenyh3
 * @create: 2025-05-29 17:25
 **/
@Slf4j
@Service(version = OuterProcurePayService.VERSION)
public class OuterProcurePayServiceImpl implements OuterProcurePayService {
	@Resource
	private RedisLockUtil redisLockUtil;
	@Resource
	private PayoutQueryService payoutQueryService;
	@Resource
	private OrderRepository orderRepository;
	@Resource
	private PayoutService payoutService;
	@Resource
	private CommonConfig commonConfig;
	@Resource
	private AccountBalanceClient accountBalanceClient;

	@Override
	@Router("#request.clientId")
	public QueryProcurePayAccountRespDTO getAccountInfo(QueryProcurePayAccountRequest request) {
		request.checkParam();
		return redisLockUtil.executeWithLock(StrUtil.format(PROCURE_ACCOUNT_QUERY_LOCK_KEY, request.getClientId()), 3_000L, () -> {

            String bizId = AccountTypeEnum.getRegulatoryBizId(request.getProxyContentId(), CurrencyEnum.CNH.getCode());
			AccountBalanceInfoResponse accountBalanceInfo = accountBalanceClient.queryRegulatoryBalanceByBizIds(request.getClientId(), Collections.singletonList(bizId));

			return ProcurePayConverter.buildProcureAccountInfoRespDTO(accountBalanceInfo);
		});
	}

	@Override
	@Router("#request.clientId")
	public ProcurePayCalculationRespDTO calculation(ProcurePayCalculationRequest request) {
		request.checkParam();
		return redisLockUtil.executeWithLock(StrUtil.format(TRAIL_TRANSACTION_KEY_FORMAT, request.getClientId(), request.getRequestId()), 10_000L, () -> {
			// 参数装换
			OrderCaculateRequest orderCaculateRequest = ProcurePayConverter.getOrderCaculateRequest(request, commonConfig.getMyBankClientId());

			// 现有计费逻辑
			OrderCalculateResponse orderCalculateResponse = null;
            try {
                orderCalculateResponse = payoutQueryService.calculateOrderFee(orderCaculateRequest);
            } catch (BizException e) {
                if (StrUtil.equalsAnyIgnoreCase(e.getMessage(), "Insufficient balance")) {
                    throw OuterErrorCode.BIZ_BREAK.exception(e.getMessage());
                }
                throw e;
            }

			// 将试算结果缓存
            ProcurePayTrailCacheDTO trailCacheDTO = ProcurePayConverter.buildTrailCacheDTO(request, orderCalculateResponse);
			String key = RedisKeyUtil.buildTrailKey(request.getClientId(), trailCacheDTO.getInquiryNo());
			RedisUtil.set(key, JSON.toJSONString(trailCacheDTO), 5 * 60); // 有效期5分钟

			// 响应装换
			ProcurePayCalculationRespDTO calculateRespDTO = ProcurePayConverter.getPreCalculateRespDTO(request, orderCalculateResponse);

			return calculateRespDTO;
		});
	}

	@Override
	@Router("#request.clientId")
	public ProcurePayCreateOrderRespDTO createOrder(ProcurePayCreateOrderRequest request) {
		request.checkParam();
		String redisKey = RedisKeyUtil.buildTrailKey(request.getClientId(), request.getFeeInquiryNo());
		try {
			return redisLockUtil.executeWithLock(StrUtil.format(TRANSACTION_KEY_FORMAT, request.getClientId(), request.getFeeInquiryNo()),
					10_000L,
					() -> {
						// 只要外部单号已存在交易，则直接报错
						Order existOrder = orderRepository.getByOutOrderId(request.getClientId(), request.getPartnerTransactionId());
						OuterErrorCode.REPEAT_REQUEST.warnIsTrue(existOrder == null, "partner_transaction_id has been exist");

						// 获取试算参数
						String trailCacheStr = RedisUtil.get(redisKey);
						log.info("代扣试算缓存信息: {}", trailCacheStr);
						OuterErrorCode.BIZ_BREAK.warnIsTrue(StrUtil.isNotBlank(trailCacheStr), "The fee inquiry has expired. Please try again later");
						ProcurePayTrailCacheDTO trailCacheDTO = JSON.parseObject(trailCacheStr, ProcurePayTrailCacheDTO.class);
						OuterErrorCode.BIZ_BREAK.warnIsTrue(trailCacheDTO != null, "The fee inquiry has expired. Please try again later");

						// 下单请求参数和试算参数不一致校验
                        this.checkCreateOrderParam(request, trailCacheDTO);

						// 下单
						OrderPayRequest orderPayRequest = ProcurePayConverter.getOrderPayRequest(request, trailCacheDTO, commonConfig.getMyBankClientId());

						OrderDTO orderDTO = null;
						try {
							orderDTO = payoutService.createOrder(orderPayRequest);
						} catch (BizException e) {
							if (StrUtil.containsAny(e.getMessage(), "direct", "balance")) {
								throw OuterErrorCode.BIZ_BREAK.exception(e.getMessage());
							}
							if (StrUtil.containsAny(e.getMessage(), USER_OPERATION_NOT_ALLOWED)) {
								log.info("网商代扣下单失败，用户被风控. clientId={}", request.getClientId());
								throw OuterErrorCode.FORBIDDEN.exception(e.getMessage());
							}
							throw e;
						}
						return ProcurePayConverter.buildProcurePayCreateOrderRespDTO(orderDTO, request);
					});
		} finally {
			RedisUtil.del(redisKey);
		}
	}

	// 校验下单接口和试算接口的请求参数是否一致
    private void checkCreateOrderParam(ProcurePayCreateOrderRequest request, ProcurePayTrailCacheDTO trailCacheDTO) {
		String errorMsg = "Illegal request";
		OuterErrorCode.BIZ_BREAK.warnIsTrue(request.getProxyContentId().equals(trailCacheDTO.getProxyContentId()), errorMsg);
		OuterErrorCode.BIZ_BREAK.warnIsTrue(request.getOrderType().equals(trailCacheDTO.getOrderType()), errorMsg);
		OuterErrorCode.BIZ_BREAK.warnIsTrue(request.getPayCurrency().equals(trailCacheDTO.getPayCurrency()), errorMsg);
		OuterErrorCode.BIZ_BREAK.warnIsTrue(request.getTransCurrency().equals(trailCacheDTO.getTransCurrency()), errorMsg);
		OuterErrorCode.BIZ_BREAK.warnIsTrue(request.getPayAmount().compareTo(trailCacheDTO.getPayAmount())==0, errorMsg);
		OuterErrorCode.BIZ_BREAK.warnIsTrue(request.getTransAmount().compareTo(trailCacheDTO.getTransAmount())==0, errorMsg);
		OuterErrorCode.BIZ_BREAK.warnIsTrue(request.getPartnerFeeRate().compareTo(trailCacheDTO.getPartnerFeeRate())==0, errorMsg);
    }

	@Override
	@Router("#request.clientId")
	public ProcurePayTransactionRespDTO queryTransactions(ProcurePayTransactionRequest request) {
		request.checkParam();
		return redisLockUtil.executeWithLock(StrUtil.format(PROCURE_PAY_TRANSACTION_QUERY_LOCK_KEY, request.getClientId(), request.getPartnerTransactionId()), 3_000L, () -> {

            Order order = orderRepository.getByOutOrderId(request.getClientId(), request.getPartnerTransactionId());
			OuterErrorCode.NOT_FOUND.warnIsTrue(order != null, "transaction not exist");

			// 响应装换
			return ProcurePayConverter.buildProcureTransactionRespDTO(order, request);
		});
	}

	@Override
	@Router("#request.myBankClientId")
	public ProcurePayRefundRespDTO refund(ProcurePayRefundRequest request) {
		request.checkParam();
		String originPartnerTransactionId = request.getOriginPartnerTransactionId();
		return redisLockUtil.executeWithLock(StrUtil.format(PROCURE_PAY_REFUND_LOCK_KEY, request.getTargetClientId(), originPartnerTransactionId),
				10_000L,
				() -> {
					// 判断被代扣方的代扣交易是否存在
					OrderDTO originOrder = payoutQueryService.getOrderByOutOrderId(request.getTargetClientId(), originPartnerTransactionId);
					OuterErrorCode.NOT_FOUND.warnIsTrue(originOrder != null, "origin transaction not exist");
					OuterErrorCode.NOT_FOUND.warnIsTrue(originOrder.getStatus().equalsIgnoreCase(OrderStatus.SUCCESS.name()), "origin transaction not success");

					// 判断被代扣方的代扣退款交易是否已存在(只要外部单号已存在交易，则直接报错)
					Order existOrder = orderRepository.getByOutOrderId(request.getMyBankClientId(), request.getPartnerTransactionId());
                    OuterErrorCode.REPEAT_REQUEST.warnIsTrue(existOrder == null, "refund transaction has been exist");

					// 下单
					OrderPayRequest orderPayRequest = ProcurePayConverter.buildOrderRefundRequest(request, commonConfig.getMyBankClientId(), originOrder.getOrderId());

					OrderDTO orderDTO = null;
					try {
						orderDTO = payoutService.createOrder(orderPayRequest);
					} catch (BizException e) {
						if (StrUtil.containsAny(e.getMessage(), "direct", "refund", "balance")) {
							throw OuterErrorCode.BIZ_BREAK.exception(e.getMessage());
						}
						throw e;
					}
					return ProcurePayConverter.buildOrderRefundRespDTO(orderDTO, request);
				});
	}

	@Override
	@Router("#request.clientId")
	public ProcurePayRefundTransactionRespDTO queryRefundTransactions(ProcurePayRefundTransactionRequest request) {
		request.checkParam();
		return redisLockUtil.executeWithLock(StrUtil.format(PROCURE_REFUND_TRANSACTION_QUERY_LOCK_KEY, request.getClientId(), request.getPartnerTransactionId()), 3_000L, () -> {

			// 通过网商侧业务单号查询代扣退款单号
			Order order = orderRepository.getByOutOrderId(request.getClientId(), request.getPartnerTransactionId());
			OuterErrorCode.NOT_FOUND.warnIsTrue(order != null, "transaction not exist");

			// 响应装换
			return ProcurePayConverter.buildProcureRefundTransactionRespDTO(order, request);
		});
	}
}

"""


class JavaAnalyzer:
    def __init__(self, filepath, source_code):
        self.filepath = filepath
        self.source_code = source_code
        self.source_lines = source_code.split("\n")
        self.nodes = []
        self.package_name = ""
        self.imports = []
        self.inheritance_info = []

    def _extract_code_block(self, start_line, end_line):
        """提取指定行范围的代码"""
        if start_line <= 0 or start_line > len(self.source_lines):
            return ""

        start_idx = start_line - 1
        end_idx = min(end_line, len(self.source_lines))

        return "\n".join(self.source_lines[start_idx:end_idx])

    def _get_end_line_number(self, node, start_line):
        """通过遍历AST获取节点的准确结束行号"""
        try:
            # 对于方法声明节点
            if isinstance(node, javalang.tree.MethodDeclaration):
                # 检查是否有方法体
                if hasattr(node, "body") and node.body is not None:
                    # 有方法体的方法，需要通过括号匹配找到结束位置
                    brace_count = 0
                    found_start = False
                    for i in range(start_line - 1, len(self.source_lines)):
                        line = self.source_lines[i]
                        for char in line:
                            if char == "{":
                                brace_count += 1
                                found_start = True
                            elif char == "}":
                                brace_count -= 1
                                if found_start and brace_count == 0:
                                    return i + 1
                    # 如果没找到，使用AST遍历的结果
                else:
                    # 接口方法（无方法体），通过查找分号来定位结束
                    for i in range(
                        start_line - 1, min(start_line + 5, len(self.source_lines))
                    ):
                        if ";" in self.source_lines[i]:
                            return i + 1
                    return start_line

            # 收集该节点所有子节点的位置信息
            max_line = start_line

            # 遍历节点的所有属性，找到最大的行号
            def traverse_node(n, depth=0):
                nonlocal max_line
                if depth > 10:  # 防止递归过深
                    return

                # 获取当前节点的位置
                if hasattr(n, "position") and n.position and n.position.line:
                    max_line = max(max_line, n.position.line)

                # 遍历节点的children
                if hasattr(n, "children"):
                    for child in n.children:
                        if child is not None:
                            if isinstance(child, list):
                                for item in child:
                                    if item is not None:
                                        traverse_node(item, depth + 1)
                            elif hasattr(child, "position") or hasattr(
                                child, "children"
                            ):
                                traverse_node(child, depth + 1)

                # 特别处理body属性
                if hasattr(n, "body"):
                    if isinstance(n.body, list):
                        for item in n.body:
                            if item is not None:
                                traverse_node(item, depth + 1)
                    elif n.body is not None:
                        traverse_node(n.body, depth + 1)

            traverse_node(node)

            # 对于有body的节点（类、接口），需要找到闭合括号
            if hasattr(node, "body") and isinstance(
                node,
                (javalang.tree.ClassDeclaration, javalang.tree.InterfaceDeclaration),
            ):
                # 从最大行号开始往后查找闭合括号
                brace_count = 0
                found_start = False
                for i in range(start_line - 1, len(self.source_lines)):
                    line = self.source_lines[i]
                    for char in line:
                        if char == "{":
                            brace_count += 1
                            found_start = True
                        elif char == "}":
                            brace_count -= 1
                            if found_start and brace_count == 0:
                                return i + 1
                # 如果没找到，返回最大行号+1
                return min(max_line + 1, len(self.source_lines))

            # 对于字段声明，查找分号
            if isinstance(node, javalang.tree.FieldDeclaration):
                for i in range(
                    start_line - 1, min(start_line + 3, len(self.source_lines))
                ):
                    if ";" in self.source_lines[i]:
                        return i + 1
                return start_line

            # 对于其他节点类型，返回找到的最大行号
            return max(max_line, start_line)

        except Exception as e:
            print(f"Error getting end line for node: {e}")
            return min(start_line + 5, len(self.source_lines))

    def _get_line_number(self, node):
        """获取节点的行号"""
        try:
            if hasattr(node, "position") and node.position:
                return node.position.line
        except:
            pass
        return 1

    def _extract_method_calls_from_node(self, node):
        """从节点中提取方法调用"""
        calls = []

        def collect_calls(n):
            if isinstance(n, javalang.tree.MethodInvocation):
                calls.append(n.member)
            elif isinstance(n, javalang.tree.ClassCreator):
                # 构造函数调用
                if hasattr(n, "type") and hasattr(n.type, "name"):
                    calls.append(n.type.name)

            # 递归遍历子节点
            if hasattr(n, "children"):
                for child in n.children:
                    if child:
                        if isinstance(child, list):
                            for item in child:
                                if item:
                                    collect_calls(item)
                        else:
                            collect_calls(child)

        try:
            collect_calls(node)
        except Exception as e:
            print(f"Error extracting method calls: {e}")

        return calls

    def _visit_method(self, node, parent_class):
        """访问方法声明"""
        method_name = node.name
        full_method_name = f"{parent_class}.{method_name}"
        if full_method_name == "PayoutService.declineOnlineOrders":
            print(1)

        start_line = self._get_line_number(node)
        end_line = self._get_end_line_number(node, start_line)
        code_block = self._extract_code_block(start_line, end_line)

        # 获取参数信息
        parameters = []
        if hasattr(node, "parameters") and node.parameters:
            for param in node.parameters:
                parameters.append(
                    {
                        "name": param.name,
                        "type": str(param.type) if param.type else "unknown",
                    }
                )

        # 提取方法调用
        method_calls = self._extract_method_calls_from_node(node)

        method_node = {
            "name": full_method_name,
            "type": "",
            "code": code_block,
            "start_line": start_line,
            "end_line": end_line,
            "parameters": parameters,
            "return_type": str(node.return_type) if node.return_type else "void",
            "modifiers": [str(mod) for mod in node.modifiers] if node.modifiers else [],
            "method_calls": method_calls,
        }
        self.nodes.append(method_node)

    def _visit_interface(self, node):
        """访问接口声明"""
        interface_name = node.name
        if interface_name == "declineOnlineOrders":
            print(1)

        start_line = self._get_line_number(node)
        end_line = self._get_end_line_number(node, start_line)
        code_block = self._extract_code_block(start_line, end_line)

        # 接口可以extends其他接口
        extends_interfaces = []
        if hasattr(node, "extends") and node.extends:
            extends_interfaces = [ext.name for ext in node.extends]

        interface_node = {
            "name": interface_name,
            "type": "class",
            "code": code_block,
            "start_line": start_line,
            "end_line": end_line,
            "extends": extends_interfaces,
            "is_interface": True,
            "modifiers": [str(mod) for mod in node.modifiers] if node.modifiers else [],
        }
        self.nodes.append(interface_node)

        # 记录继承信息
        for parent_interface in extends_interfaces:
            self.inheritance_info.append((interface_name, parent_interface, "extends"))

        # 处理接口方法
        if hasattr(node, "body"):
            for member in node.body:
                if isinstance(member, javalang.tree.MethodDeclaration):
                    self._visit_method(member, interface_name)

    def _visit_type(self, node):
        """访问类型声明节点"""
        if isinstance(node, javalang.tree.ClassDeclaration):
            self._visit_class(node)
        elif isinstance(node, javalang.tree.InterfaceDeclaration):
            self._visit_interface(node)
        elif isinstance(node, javalang.tree.EnumDeclaration):
            self._visit_enum(node)

    def _visit_class(self, node):
        """访问类声明"""
        class_name = node.name

        start_line = self._get_line_number(node)
        end_line = self._get_end_line_number(node, start_line)
        code_block = self._extract_code_block(start_line, end_line)

        # 获取继承信息
        extends_class = None
        if hasattr(node, "extends") and node.extends:
            extends_class = node.extends.name

        implements_interfaces = []
        if hasattr(node, "implements") and node.implements:
            implements_interfaces = [impl.name for impl in node.implements]

        class_node = {
            "name": class_name,
            "type": "class",
            "code": code_block,
            "start_line": start_line,
            "end_line": end_line,
            "extends": extends_class,
            "implements": implements_interfaces,
            "is_interface": False,
            "modifiers": [str(mod) for mod in node.modifiers] if node.modifiers else [],
        }
        self.nodes.append(class_node)

        # 记录继承信息
        if extends_class:
            self.inheritance_info.append((class_name, extends_class, "extends"))
        for impl_interface in implements_interfaces:
            self.inheritance_info.append((class_name, impl_interface, "implements"))

        # 处理类成员
        if hasattr(node, "body"):
            for member in node.body:
                if isinstance(member, javalang.tree.MethodDeclaration):
                    self._visit_method(member, class_name)

    def _visit_enum(self, node):
        """访问枚举声明"""
        enum_name = node.name

        start_line = self._get_line_number(node)
        end_line = self._get_end_line_number(node, start_line)
        code_block = self._extract_code_block(start_line, end_line)

        enum_node = {
            "name": enum_name,
            "type": "enum",
            "code": code_block,
            "start_line": start_line,
            "end_line": end_line,
            "modifiers": [str(mod) for mod in node.modifiers] if node.modifiers else [],
        }
        self.nodes.append(enum_node)

    def analyze(self, tree):
        """分析Java语法树并提取节点"""
        if hasattr(tree, "package") and tree.package:
            self.package_name = tree.package.name

        # 提取import信息
        if hasattr(tree, "imports"):
            for import_decl in tree.imports:
                import_info = {
                    "path": import_decl.path,
                    "static": getattr(import_decl, "static", False),
                    "wildcard": getattr(import_decl, "wildcard", False),
                }
                self.imports.append(import_info)
                print(f"Found import: {import_info}")  # 调试信息

        # 分析类型声明
        if hasattr(tree, "types"):
            for type_decl in tree.types:
                self._visit_type(type_decl)

        return self.nodes


tree = javalang.parse.parse(java_code)
analyzer = JavaAnalyzer("filepath", java_code)
nodes = analyzer.analyze(tree)
print("\n分析结果：")
print("=" * 80)
for node in nodes[:5]:  # 只打印前5个节点作为示例
    print(f"节点: {node['name']}")
    print(f"  类型: {node['type']}")
    print(f"  起始行: {node['start_line']}")
    print(f"  结束行: {node['end_line']}")
    print(f"  行数: {node['end_line'] - node['start_line'] + 1}")
print("\n总节点数:", len(nodes))
